﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections.ObjectModel;
using System.Windows.Input;
using System.Diagnostics.Contracts;
using System.Windows;
using ShredTheLog.Common.MVVM;
using ShredTheLog.Api;
using ShredTheLog.Controls;
using System.ComponentModel.Composition;

namespace ShredTheLog.ViewModels
{
    public sealed class SettingsViewModel : ViewModelBase
    {
        [Import]
        private IShredServices ShredServices { get; set; }

        [ImportMany]
        private Lazy<ISettingsPageViewModel, IOrderMetadata>[] _pages = null;
        public IEnumerable<ISettingsPageViewModel> Pages
        {
            get 
            {
                Contract.Assume(_pages != null);
                return from i in _pages orderby i.Metadata.Order select i.Value; 
            }
        }

        private bool _isWindowClosed;
        public bool IsWindowClosed
        {
            get { return _isWindowClosed; }
            set
            {
                _isWindowClosed = value;
                OnPropertyChanged(() => IsWindowClosed);
            }
        }

        private Action ApplySettingsAction
        {
            get
            {
                return () =>
                {
                    Pages.ForEach(
                        p =>
                        {
                            try
                            {
                                p.Save();
                            }
                            catch (Exception ex)
                            {
                                throw new SettingsSaveException(p, ex);
                            }
                        });
                    ShredServices.SaveSettings();
                };
            }
        }

        private ICommand _saveCommand;
        public ICommand SaveCommand
        {
            get
            {
                if (_saveCommand == null)
                {
                    _saveCommand = new DelegateCommand(
                        () =>
                        {
                            try
                            {
                                ViewFactory.RecreateViews(ApplySettingsAction);
                                IsWindowClosed = true;
                            }
                            catch (Exception ex)
                            {
                                ProcessSaveException(ex);
                            }
                        });
                }
                return _saveCommand;
            }
        }

        private ICommand _applyCommand;
        public ICommand ApplyCommand
        {
            get
            {
                if (_applyCommand == null)
                {
                    _applyCommand = new DelegateCommand(
                        () =>
                            {
                                try
                                {
                                    ViewFactory.RecreateViews(ApplySettingsAction);
                                }
                                catch (Exception ex)
                                {
                                    ProcessSaveException(ex);
                                }
                            });
                }
                return _applyCommand;
            }
        }

        private ICommand _closeCommand;
        public ICommand CloseCommand
        {
            get
            {
                if (_closeCommand == null)
                {
                    _closeCommand = new DelegateCommand(() => { IsWindowClosed = true; });
                }
                return _closeCommand;
            }
        }

        private void ProcessSaveException(Exception ex)
        {
            Contract.Requires(ex != null);

            if (ex is AggregateException)
            {
                var aex = (AggregateException)ex;
                Contract.Assume(aex.InnerExceptions != null);
                aex.InnerExceptions.ForEach(iex => ProcessSaveException(iex));
            }
            else if (ex is SettingsSaveException)
                ShredServices.ShowError(() => String.Format(ShredServices.GetLocalizedString("s_FailedToApplySettings2"), ((SettingsSaveException)ex).Page.Header), ex);
            else
                ShredServices.ShowError(() => ShredServices.GetLocalizedString("s_FailedToApplySettings"), ex);
        }

        public sealed class SettingsSaveException : ApplicationException
        {
            public ISettingsPageViewModel Page { get; private set; }

            public SettingsSaveException(ISettingsPageViewModel page, Exception innerException)
                : base("Failed to apply settings", innerException)
            {
                Contract.Requires(page != null);
                Contract.Requires(innerException != null);

                Page = page;
            }
        }
    }
}
